



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>其他模块 (40%) &mdash; Nginx开发从入门到精通</title>
    <link rel="stylesheet" href="_static/book.css" type="text/css" />
    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
    <link rel="stylesheet" href="_static/comment.css" type="text/css" />
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    '',
        VERSION:     '0.1',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="../js/??ga.js,correctpng.js?v=1"></script>
    <script type="text/javascript" src="_static/jquery.js"></script>
    <script type="text/javascript" src="_static/underscore.js"></script>
    <script type="text/javascript" src="_static/doctools.js"></script>
    <script type="text/javascript" src="_static/booktools.js"></script>
    <script type="text/javascript" src="_static/translations.js"></script>
    <script type="text/javascript" src="_static/comment.js"></script>
    <link rel="top" title="Nginx开发从入门到精通" href="index.html" />
    <link rel="up" title="上篇：nginx模块开发篇" href="module_development.html" />
    <link rel="next" title="模块开发高级篇(30%)" href="chapter_7.html" />
    <link rel="prev" title="upstream模块" href="chapter_5.html" /> 
  </head>
  <body lang="zh-Hans">
    <div class="related">
      <h3>导航</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="总目录"
             accesskey="I">索引</a></li>
        <li class="right" >
          <a href="chapter_7.html" title="模块开发高级篇(30%)"
             accesskey="N">下一页</a> |</li>
        <li class="right" >
          <a href="chapter_5.html" title="upstream模块"
             accesskey="P">上一页</a> |</li>
        <li><a href="index.html">Nginx开发从入门到精通</a> &raquo;</li>
          <li><a href="module_development.html" accesskey="U">上篇：nginx模块开发篇</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="id1">
<h1>其他模块 (40%)<a class="headerlink" href="#id1" title="永久链接至标题">¶</a></h1>
<p>Nginx的模块种类挺多的，除了HTTP模块，还有一些核心模块和mail系列模块。核心模块主要是做一些基础功能，比如Nginx的启动初始化，event处理机制，错误日志的初始化，ssl的初始化，正则处理初始化。</p>
<p>mail模块可以对imap，pop3，smtp等协议进行反向代理，这些模块本身不对邮件内容进行处理。</p>
<div class="section" id="core-40">
<h2>core模块 (40%)<a class="headerlink" href="#core-40" title="永久链接至标题">¶</a></h2>
<div class="section" id="nginx-40">
<h3>Nginx的启动模块 (40%)<a class="headerlink" href="#nginx-40" title="永久链接至标题">¶</a></h3>
<p>启动模块从启动Nginx进程开始，做了一系列的初始化工作，源代码位于src/core/nginx.c，从main函数开始:</p>
<ol class="arabic simple">
<li>时间、正则、错误日志、ssl等初始化</li>
<li>读入命令行参数</li>
<li>OS相关初始化</li>
<li>读入并解析配置</li>
<li>核心模块初始化</li>
<li>创建各种暂时文件和目录</li>
<li>创建共享内存</li>
<li>打开listen的端口</li>
<li>所有模块初始化</li>
<li>启动worker进程</li>
</ol>
</div>
</div>
<div class="section" id="event-40">
<h2>event模块 (40%)<a class="headerlink" href="#event-40" title="永久链接至标题">¶</a></h2>
<div class="section" id="id2">
<h3>event的类型和功能 (40%)<a class="headerlink" href="#id2" title="永久链接至标题">¶</a></h3>
<p>Nginx是以event（事件）处理模型为基础的模块。它为了支持跨平台，抽象出了event模块。它支持的event处理类型有：AIO（异步IO），/dev/pool（Solaris 和Unix特有），epoll（Linux特有），eventport（Solaris 10特有），kqueue（BSD特有），pool，rtsig（实时信号），select等。</p>
<p>event模块的主要功能就是，监听accept后建立的连接，对读写事件进行添加删除。事件处理模型和Nginx的非阻塞IO模型结合在一起使用。当IO可读可写的时候，相应的读写时间就会被唤醒，此时就会去处理事件的回调函数。</p>
<p>特别对于Linux，Nginx采用的是epoll的EPOLLET（边沿触发）的方法来触发事件，而不是EPOLLLT（水平触发），所以如果出现了可读事件，进行处理时，必须读取所有的可读数据，否则可能会出现读事件不再触发，连接饿死的情况。</p>
<div class="highlight-none"><div class="highlight"><pre>typedef struct {
        /* 添加删除事件 */
        ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
        ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

        ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
        ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

        /* 添加删除连接，会同时监听读写事件 */
        ngx_int_t  (*add_conn)(ngx_connection_t *c);
        ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

        ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
        /* 处理事件的函数 */
        ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                                   ngx_uint_t flags);

        ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
        void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
</pre></div>
</div>
<p>上述是event处理抽象出来的关键结构体，可以看到，每个event处理模型，都需要实现部分功能。最关键的是add和del功能，就是最基本的添加和删除事件的函数。</p>
</div>
<div class="section" id="accept-40">
<h3>accept锁 (40%)<a class="headerlink" href="#accept-40" title="永久链接至标题">¶</a></h3>
<p>Nginx是多进程程序，80端口是各进程所共享的，多进程同时listen 80端口，势必会产生竞争，也产生了所谓的“惊群”效应。当内核accept一个连接时，会唤醒所有等待中的进程，但实际上只有一个进程能获取连接，其他的进程都是被无效唤醒的。所以Nginx采用了自有的一套accept加锁机制，避免多个进程同时调用accept。Nginx多进程的锁在底层默认是通过CPU自旋锁来实现。如果操作系统不支持自旋锁，就采用文件锁。</p>
<p>Nginx事件处理的入口函数是ngx_process_events_and_timers()，下面是部分代码，可以看到其加锁的过程：</p>
<div class="highlight-none"><div class="highlight"><pre>if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled &gt; 0) {
                ngx_accept_disabled--;

        } else {
                if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                        return;
                }

                if (ngx_accept_mutex_held) {
                        flags |= NGX_POST_EVENTS;

                } else {
                        if (timer == NGX_TIMER_INFINITE
                                || timer &gt; ngx_accept_mutex_delay)
                        {
                                timer = ngx_accept_mutex_delay;
                        }
                }
        }
}
</pre></div>
</div>
<p>在ngx_trylock_accept_mutex()函数里面，如果拿到了锁，Nginx会把listen的端口读事件加入event处理，该进程在有新连接进来时就可以进行accept了。注意accept操作是一个普通的读事件。下面的代码说明了这点：</p>
<div class="highlight-none"><div class="highlight"><pre>(void) ngx_process_events(cycle, timer, flags);

if (ngx_posted_accept_events) {
        ngx_event_process_posted(cycle, &amp;ngx_posted_accept_events);
}

if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&amp;ngx_accept_mutex);
}
</pre></div>
</div>
<p>ngx_process_events()函数是所有事件处理的入口，它会遍历所有的事件。抢到了accept锁的进程跟一般进程稍微不同的是，它的被加上了NGX_POST_EVENTS标志，也就是说在ngx_process_events() 函数里面只接受而不处理事件，并加入post_events的队列里面。直到ngx_accept_mutex锁去掉以后才去处理具体的事件。为什么这样？因为ngx_accept_mutex是全局锁，这样做可以尽量减少该进程抢到锁以后，从accept开始到结束的时间，以便其他进程继续接收新的连接，提高吞吐量。</p>
<p>ngx_posted_accept_events和ngx_posted_events就分别是accept延迟事件队列和普通延迟事件队列。可以看到ngx_posted_accept_events还是放到ngx_accept_mutex锁里面处理的。该队列里面处理的都是accept事件，它会一口气把内核backlog里等待的连接都accept进来，注册到读写事件里。</p>
<p>而ngx_posted_events是普通的延迟事件队列。一般情况下，什么样的事件会放到这个普通延迟队列里面呢？我的理解是，那些CPU耗时比较多的都可以放进去。因为Nginx事件处理都是根据触发顺序在一个大循环里依次处理的，因为Nginx一个进程同时只能处理一个事件，所以有些耗时多的事件会把后面所有的事件处理都被耽搁了。</p>
<p>除了加锁，Nginx也对各进程的请求处理的均衡性作了优化，也就是说，如果在负载高的时候，进程抢到的锁过多，会导致这个进程被禁止接受请求一段时间。</p>
<p>比如，在ngx_event_accept函数中，有类似代码：</p>
<div class="highlight-none"><div class="highlight"><pre>ngx_accept_disabled = ngx_cycle-&gt;connection_n / 8
              - ngx_cycle-&gt;free_connection_n;
</pre></div>
</div>
<p>ngx_cycle-&gt;connection_n是进程可以分配的连接总数，ngx_cycle-&gt;free_connection_n是空闲的进程数。上述等式说明了，当前进程的空闲进程数小于1/8的话，就会被禁止accept一段时间。</p>
</div>
<div class="section" id="id3">
<h3>定时器 (40%)<a class="headerlink" href="#id3" title="永久链接至标题">¶</a></h3>
<p>Nginx在需要用到超时的时候，都会用到定时器机制。比如，建立连接以后的那些读写超时。Nginx使用红黑树来构造定期器，红黑树是一种有序的二叉平衡树，其查找插入和删除的复杂度都为O(logn)，所以是一种比较理想的二叉树。</p>
<p>定时器的机制就是，二叉树的值是其超时时间，每次查找二叉树的最小值，如果最小值已经过期，就删除该节点，然后继续查找，直到所有超时节点都被删除。</p>
</div>
</div>
<div class="section" id="mail">
<h2>mail模块<a class="headerlink" href="#mail" title="永久链接至标题">¶</a></h2>
<div class="section" id="id4">
<h3>mail模块的实现<a class="headerlink" href="#id4" title="永久链接至标题">¶</a></h3>
</div>
<div class="section" id="id5">
<h3>mail模块的功能<a class="headerlink" href="#id5" title="永久链接至标题">¶</a></h3>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="http://tengine.taobao.org/">
              <img class="logo" src="_static/logo.png" alt="Logo"/>
            </a></p>
  <h3><a href="index.html">內容目录</a></h3>
  <ul>
<li><a class="reference internal" href="#">其他模块 (40%)</a><ul>
<li><a class="reference internal" href="#core-40">core模块 (40%)</a><ul>
<li><a class="reference internal" href="#nginx-40">Nginx的启动模块 (40%)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#event-40">event模块 (40%)</a><ul>
<li><a class="reference internal" href="#id2">event的类型和功能 (40%)</a></li>
<li><a class="reference internal" href="#accept-40">accept锁 (40%)</a></li>
<li><a class="reference internal" href="#id3">定时器 (40%)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#mail">mail模块</a><ul>
<li><a class="reference internal" href="#id4">mail模块的实现</a></li>
<li><a class="reference internal" href="#id5">mail模块的功能</a></li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>上一个主题</h4>
  <p class="topless"><a href="chapter_5.html"
                        title="上一章">upstream模块</a></p>
  <h4>下一个主题</h4>
  <p class="topless"><a href="chapter_7.html"
                        title="下一章">模块开发高级篇(30%)</a></p>
  <h3>本页</h3>
  <ul class="this-page-menu">
    <li><a href="_sources/chapter_6.txt"
           rel="nofollow">显示源代码</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>快速搜索</h3>
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="搜索" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    输入相关的模块，术语，类或者函数名称进行搜索
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>导航</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="总目录"
             >索引</a></li>
        <li class="right" >
          <a href="chapter_7.html" title="模块开发高级篇(30%)"
             >下一页</a> |</li>
        <li class="right" >
          <a href="chapter_5.html" title="upstream模块"
             >上一页</a> |</li>
        <li><a href="index.html">Nginx开发从入门到精通</a> &raquo;</li>
          <li><a href="module_development.html" >上篇：nginx模块开发篇</a> &raquo;</li> 
      </ul>
    </div>
    <div class="footer">
        &copy; 版权所有 2012, taobao.
      使用 <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3.
    </div>
  </body>
</html>